using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Xml;

using Skeletons;

namespace Rigging
{
	public class BLine : RiggingEntity
	{
		private Color m_clrPen = Color.Black;
        private Color m_clrFill = Color.Gray;
		private float m_fPenWidth = 1F;
		private int m_iActivePointIdx = -1; // 0,1,2,3 or -1 (none)
		private Point m_pntDragReference;

		public BLine(Skeletons.Bone parent, Color outlineClr, Color fillClr, float width) : base()
		{
			m_bone = parent;
			m_clrPen = outlineClr;
            m_clrFill = fillClr;
			m_fPenWidth = width;
			m_iActivePointIdx = -1;
		}

		public List<Point> Points
		{
            get { return m_points; }
            set { m_points = value; }
		}

		public override bool Selected
		{
			get { return m_selected; }
			set
			{
				m_selected = value;
				if(!m_selected)
					m_iActivePointIdx = -1;
			}
		}

		public override bool IsUnder(Point p,int treshold)
		{
            Point pt1 = m_points[0];
            Point pt2 = m_points.Count > 1 ? m_points[1] : pt1;
            Point pt3 = m_points.Count > 2 ? m_points[2] : pt2;
            Point pt4 = m_points.Count > 3 ? m_points[3] : pt3;

			if ((Math.Abs(pt1.X - p.X) < treshold) && (Math.Abs(pt1.Y - p.Y) < treshold))
			{
				m_iActivePointIdx = 0;
				return true;
			}
			if ((Math.Abs(pt2.X - p.X) < treshold) && (Math.Abs(pt2.Y - p.Y) < treshold))
			{
				m_iActivePointIdx = 1;
				return true;
			}
			if ((Math.Abs(pt3.X - p.X) < treshold) && (Math.Abs(pt3.Y - p.Y) < treshold))
			{
				m_iActivePointIdx = 2;
				return true;
			}
			if ((Math.Abs(pt4.X - p.X) < treshold) && (Math.Abs(pt4.Y - p.Y) < treshold))
			{
				m_iActivePointIdx = 3;
				return true;
			}

			m_pntDragReference = p;
			m_iActivePointIdx = -1;


			for (float t = 0; t < 1.0; t += 0.01F)
			{
				float fW = 1 - t;
				float fA = fW * fW * fW;
				float fB = 3 * t * fW * fW;
				float fC = 3 * t * t * fW;
				float fD = t * t * t;
				float fX = fA * pt1.X + fB * pt2.X + fC * pt3.X + fD * pt4.X;

				if(Math.Abs(p.X - fX) > treshold)
					continue;

				float fY = fA * pt1.Y + fB * pt2.Y + fC * pt3.Y + fD * pt4.Y;

				if(Math.Abs(p.Y - fY) <= treshold)
					return true;
			}

			return false;
		}

		public override bool ContainedByRect(Matrix mt, Rectangle r)
		{
            Point[] pts = m_points.ToArray();
			mt.TransformPoints(pts);
			foreach (Point p in pts)
			{
				if (!r.Contains(p))
					return false;
			}
			return true;
		}

		public void AddPoint(Point p)
		{
            m_points.Add(p);
		}
		
		public void MoveLastPoint(Point p)
		{
            if (m_points.Count > 0)
                m_points.RemoveAt(m_points.Count - 1);
            m_points.Add(p);
		}
		
		public void MoveActivePoint(Point p)
		{
			switch(m_iActivePointIdx)
			{
				case -1:
					// move all points
					int xoffs = p.X - m_pntDragReference.X;
					int yoffs = p.Y - m_pntDragReference.Y;
                    for (int i = 0; i < m_points.Count; i++)
					{
                        Point pp = m_points[i];
						pp.X += xoffs;
						pp.Y += yoffs;
                        m_points[i] = pp;
					}
					m_pntDragReference = p;
				break;
				default:
                    if (m_points.Count < (m_iActivePointIdx + 1))
						return;
                    m_points[m_iActivePointIdx] = p;
				break;
			}
		}

		public override void LoadPoints(Point[] pts)
		{
            m_points = new List<Point>(pts);
		}

		public bool IsEmpty()
		{
            return m_points.Count < 1;
		}

		public bool IsComplete()
		{
            return m_points.Count >= 4;
		}

		public override void Repaint(Graphics gr)
		{
            if (m_points.Count < 1)
				return; // nothing to paint

            Point pt1 = m_points[0];
            Point pt2 = m_points.Count > 1 ? m_points[1] : pt1;
            Point pt3 = m_points.Count > 2 ? m_points[2] : pt2;
            Point pt4 = m_points.Count > 3 ? m_points[3] : pt3;

            // draw filling selected color
            GraphicsPath path = new GraphicsPath();
            path.AddBezier(pt1, pt2, pt3, pt4);
            gr.FillPath(new Pen(m_clrFill).Brush, path);

            // then overlay the outline            
			Pen penCurrent = new Pen(m_outlineColor.Color, m_fPenWidth);  // changed this to use outline color (black)
			
			gr.DrawBezier(penCurrent, pt1, pt2, pt3, pt4);

			if (m_selected)
			{
                // draw control lines
                penCurrent.Width = 1.0F;
                penCurrent.DashStyle = DashStyle.DashDot;

                gr.DrawLine(penCurrent, pt1, pt2);
                gr.DrawLine(penCurrent, pt3, pt4);

                // draw points
                foreach (Point p in m_points)
				{
					PaintHandle(gr, p);
				}
			}
		}

		public override string ToXmlString(string indent)
		{
            string color = m_clrFill.ToString(); //.Replace("Color [","").Replace("]","");
            string ret = indent + "<SKIN type='BLine' boneId='" + Bone.Id + "' color='" + color + "' penWidth='" + m_fPenWidth + "' >\n";
            for (int i = 0; i < m_points.Count; i++)
                ret += indent + "\t<POINT index='" + i + "' X='" + m_points[i].X + "' Y='" + m_points[i].Y + "'/>\n";
            ret += indent + "</SKIN>\n";

            return ret;
		}
	}

}
